#include "hal_gps.h"

#include <unistd.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <fcntl.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/time.h>
#include "CxlUtil.h"
#include "sqlLoop.h"
#include "pal/lane_control.h"
#include "uv.h"
#include "hal_gps_uv.h"
#include "userLog.h"
#include "minmea.h"
#include "pal/lane_cmd_parse.h"
#include "voiceChedaoji.h"
#include "configParser.h"
#include "pending_data_queue.h"
#include "hal_gps_libuv.h"

#define GPS_DEV_PATH "/dev/ttymxc4"
#define GPS_MAX_READ_COUNT 1000
// 重新解析千寻dns的时间间隔（或者说重新连接千寻服务器的间隔）
#define QX_RE_RESOLVE_TIMEOUT 1 * 1000

//车速为0的时候通知车牌上送时间间隔
#define SPEED_ZERO_NOTICE_PIC_UPLOAD_INTERVAL 5 * 1000

static uint16_t gps_read_count = 0;
static uint8_t line[MINMEA_MAX_LENGTH * 2];

static gps_t globalGps = {0};
static uv_mutex_t globalGpsLock;
static int qx_sockfd = -1;

static void on_read_dev(uv_poll_t *req, int status, int events);
static void gpsInit(gps_t *gpsData, uint8_t *gngga, uint8_t *gnrmc);
static void qx_gps_init();
static void hal_gps_write_cb(uv_write_t *req, int32_t status);
static void onQxConnect(uv_connect_t *client, int32_t status);
static void qx_recv_response(uv_stream_t *clientStream, ssize_t nread, const uv_buf_t *buf);
static void hal_gps_hard_ware_write_cb(uv_write_t *req, int32_t status);
static void qxAuth();
static double calc_gps_speed(struct minmea_float *rmc_speed);
static void notice_speed_event_is_false();
static void notice_speed_event_is_true(uv_timer_t *handle);
// static void start_notice_speed_zero_event(uv_timer_t *handle);
// static void stop_notice_speed_zero_event();
static void stop_pic_upload_event();

static bool is_gps_speed_event_true_notice = false;
static bool is_gps_speed_event_false_notice = false;
// static bool is_notice_speed_zero_event = false;

static int32_t qxFd = 0;
static bool isQxAuthPass = false;
static uint8_t qx_addr[17] = {'\0'};

static uv_timer_t qx_report_timer;

static time_t qx_last_send_time;
static time_t qx_last_recv_time;

typedef struct
{
    uv_poll_t poll_handle;
    FILE *fp;
} PollStruct;

PollStruct poll;

typedef struct _dua_gps
{
    struct minmea_float latitude;
    struct minmea_float longitude;
} dua_gps;

static dua_gps last_gps, curr_gps;
static double dua_value = 0.15; // TODO

static void set_dua_gps_value(dua_gps *gps, struct minmea_float *latitude,
                              struct minmea_float *longitude);
static double compare_dua_gps(dua_gps *last, dua_gps *curr);
static void set_gps_value_init(dua_gps *gps);

static void gpsInit(gps_t *gpsData, uint8_t *gngga, uint8_t *gnrmc)
{
    memset(gpsData, 0, sizeof(gps_t));
    headInit((head_t *)gpsData, UPLOAD_GPS_LOCATION);
    time_bcd(gpsData->gps_timeBcd);
    memcpy(gpsData->gps_gngga_data, gngga, MINMEA_MAX_LENGTH);
    memcpy(gpsData->gps_gnrmc_data, gnrmc, MINMEA_MAX_LENGTH);

    gpsData->len = CAL_FRAME_DATA_LEN_TO_NET(gps_t);
    gpsData->crc = htons(crc((uint8_t *)gpsData + CRC_BASE_ADDR(gps_t), sizeof(gps_t) - MEM_SIZE(gps_t, crc) - MEM_SIZE(gps_t, head)));
}

typedef struct _
{
    char gps_gngga_data[MINMEA_MAX_LENGTH];
    char gps_gnrmc_data[MINMEA_MAX_LENGTH];
} gps_report_data;

static gps_report_data g_gps_data;

static void reportQxServerTimeOutCb(uv_timer_t *handle)
{
    static int first = true;
    // gga上传给千寻服务器
    if (isQxAuthPass == true)
    {
        gps_t gps_data = getLastGps();
        uint8_t gps_gngga_data[MINMEA_MAX_LENGTH + 1] = {0};
        strncpy(gps_gngga_data, gps_data.gps_gngga_data, MINMEA_MAX_LENGTH);

        static uint8_t uploadDataToQxCount = 0;
        uploadDataToQxCount++;
        if (uploadDataToQxCount == 100)
        {
            LOG_INFO("上传gga数据给千寻服务器:%s", gps_gngga_data);
            uploadDataToQxCount = 0;
        }
        int32_t gngga_data_len = sizeof(gps_gngga_data);
        uint8_t *lineTmp = (uint8_t *)malloc(gngga_data_len);
        memset(lineTmp, 0, gngga_data_len);
        memcpy(lineTmp, gps_gngga_data, gngga_data_len);
        write_req_t *wr_rq = (write_req_t *)malloc(sizeof(write_req_t));
        wr_rq->buf = uv_buf_init((uint8_t *)lineTmp, gngga_data_len);
        uv_connect_t *client = gethalGpsClientConnect();
        // 传给千寻服务器的数据
        // hzlog_info(userLog, wr_rq->buf.base, wr_rq->buf.len);
        uv_write((uv_write_t *)wr_rq, client->handle, &wr_rq->buf, 1, hal_gps_write_cb);
        qx_last_send_time = time(NULL);
    }
    else
    {
        LOG_INFO("千寻服务器没有连接上，或者授权不成功");
    }
    if (first)
    {
        first = false;
        return;
    }
    int due_time = qx_last_send_time - qx_last_recv_time;
    if (isQxAuthPass && (due_time > 3 * (get_qx_config()->qx_report_interval)))
    {
        LOG_ERROR("超过三次未收到千寻服务器响应，重新链接千寻服务器");
        uv_close((uv_handle_t *)gethalGpsClientConnect()->handle, NULL);
        halGpsMainWorkSetTimeout(getQxAddrInfoAndConnect, QX_RE_RESOLVE_TIMEOUT);
        isQxAuthPass = false;
    }
}

static void on_read_dev(uv_poll_t *req, int status, int events)
{
    PollStruct *poll = (PollStruct *)req;
    static int qx_reconnect_number = 0;

    while (fgets(line, MINMEA_MAX_LENGTH * 2, poll->fp))
    {
        struct minmea_sentence_gga frame_gga;
        struct minmea_sentence_rmc frame_rmc;
        memset(&frame_gga, 0, sizeof(frame_gga));
        memset(&frame_rmc, 0, sizeof(frame_rmc));

        //  LOG_INFO("gps data:%s", line);
        switch (minmea_sentence_id(line, false))
        {
        case MINMEA_SENTENCE_GGA:
        {
            // LOG_INFO("gngga data:%s", line);
            if (minmea_parse_gga(&frame_gga, line))
            {
                gps_read_count++;
                if (gps_read_count > GPS_MAX_READ_COUNT)
                {
                    // LOG_INFO("gps设备正常运作，gps gga数据有效性：%s", frame.valid == true ? "有效" : "无效");
                    LOG_INFO("gps设备正常运作");
                    gps_read_count = 0;
                }
                strncpy(g_gps_data.gps_gngga_data, line, MINMEA_MAX_LENGTH);

                //启动标定功能
                if (getIsStartCalibrationV2())
                {
                    // set_dua_gps_value(&last_gps, &frame_gga.latitude, &frame_gga.longitude);
                    set_dua_gps_value(&curr_gps, &frame_gga.latitude, &frame_gga.longitude);
                    if (compare_dua_gps(&last_gps, &curr_gps) >=
                        get_upload_pic_config()->calibration_dua)
                    {
                        last_gps = curr_gps;
                        LOG_INFO("触发标定v2拍照");
                        dispatchTriggerToCamera();
                    }
                }
                else
                {
                    set_gps_value_init(&last_gps);
                }
            }

            else
            {
                LOG_ERROR("gga数据校验失败");
            }
            break;
        }

        case MINMEA_SENTENCE_RMC:
        {
            // LOG_INFO("gnrmc data:%s", line);
            if (minmea_parse_rmc(&frame_rmc, line))
            {
                strncpy(g_gps_data.gps_gnrmc_data, line, MINMEA_MAX_LENGTH);
                if (getIsConnectToServ())
                {
                    double speed = calc_gps_speed(&frame_rmc.speed);
                    static int calc_number = 0;
                    calc_number++;
                    if (calc_number == 3000)
                    {
                        LOG_INFO("当前速度为 %g米/秒", speed);
                        calc_number = 0;
                    }

                    if (speed <= get_upload_pic_config()->speed)
                    {
                        // LOG_INFO("当前速度为 %g米/秒", speed);
                        halGpsSpeedTimerSet(notice_speed_event_is_true, get_upload_pic_config()->keep_time * 1000);
                    }
                    else
                    {
                        stop_pic_upload_event();
                    }
                }
                else
                {
                    stop_pic_upload_event();
                }
            }
            else
            {
                LOG_ERROR("rmc数据校验失败");
            }
            break;
        }
        default:
            break;
        }
    }
    uv_mutex_lock(&globalGpsLock);
    gpsInit(&globalGps, g_gps_data.gps_gngga_data, g_gps_data.gps_gnrmc_data);
    uv_mutex_unlock(&globalGpsLock);
}

gps_t getLastGps()
{
    gps_t tmp;
    uv_mutex_lock(&globalGpsLock);
    tmp = globalGps;
    uv_mutex_unlock(&globalGpsLock);
    return tmp;
}

static void set_gps_value_init(dua_gps *gps)
{
    gps->latitude.scale = 1;
    gps->latitude.value = 0;

    gps->longitude.scale = 1;
    gps->longitude.value = 0;
    return;
}

static void set_dua_gps_value(dua_gps *gps, struct minmea_float *latitude,
                              struct minmea_float *longitude)
{
    // gps->latitude = *latitude;
    // gps->longitude = *longitude;
    gps->latitude.scale = latitude->scale;
    gps->latitude.value = latitude->value;

    gps->longitude.scale = longitude->scale;
    gps->longitude.value = longitude->value;
}

static double float_to_double(struct minmea_float val)
{
    double value = val.value;
    double scale = val.scale;
    //  LOG_INFO("value:%d, scale:%d", val.value, val.scale);
    if (val.scale == 0)
    {
        return value;
    }
    double data = value / scale;

    double dd = (int)data / 100; //得到度dd
    double mm = data - dd * 100; //得到分mm.mmmm
    double temp = mm / 60;
    double result = dd + temp;
    return result;
}

static double calc_abs(double val1, double val2)
{
    if (val1 > val2)
    {
        return val1 - val2;
    }
    return val2 - val1;
}

static double compare_dua_gps(dua_gps *last, dua_gps *curr)
{
    double last_latitude = float_to_double(last->latitude);
    double last_longitude = float_to_double(last->longitude);
    double curr_latitude = float_to_double(curr->latitude);
    double curr_longitude = float_to_double(curr->longitude);
    // LOG_INFO("当前经度:%f,当前纬度:%f,上一次拍照经度:%f,上一次拍照纬度:%f",
    //          curr_latitude, curr_longitude, last_latitude, curr_longitude);
    double result = calc_abs(last_latitude, curr_latitude) +
                    calc_abs(last_longitude, curr_longitude);
    // LOG_INFO("计算两次经纬度差值:%f", result);
    return result * 100;
}

#if 0
// TODO: 要删除的测试代码
static void startTrigger(uv_timer_t *handle)
{
    bool isStartCalibration = getIsStartCalibrationV2();
    if (isStartCalibration == true)
    {
        dispatchTriggerToCamera();
    }
}
#endif

void gps_init()
{
    qxFd = open(GPS_DEV_PATH, O_RDWR | O_NONBLOCK);
    poll.fp = fdopen(qxFd, "r");
    if (-1 == qxFd)
    {
        LOG_ERROR("打开gps设备失败");
        return;
    }

    if (setOpt(qxFd, 38400, 8, 'N', 1) == false)
    {
        return;
    }
    poll.fp = fdopen(qxFd, "r");
    int32_t poll_result = uv_poll_init(getLoop(), &poll.poll_handle, qxFd);
    if (poll_result < 0)
    {
        LOG_ERROR("监听设备失败， 原因：%s", uv_err_name(poll_result));
        return;
    }
    uv_mutex_init(&globalGpsLock);
    uv_poll_start((uv_poll_t *)(&poll), UV_READABLE, on_read_dev);
#if 0
    // TODO: 要删除的测试代码
    uv_timer_t *testCalibrationTimer = timerDequeue();
    uv_timer_start(testCalibrationTimer, startTrigger, 0, 2 * 1000);
#endif
}

static uv_getaddrinfo_t resolver;
static void onQxDNSResolve(uv_getaddrinfo_t *handle, int status, struct addrinfo *response);
void getQxAddrInfoAndConnect(uv_timer_t *handle)
{
    QxConfig *qx_cfg = get_qx_config();
    uint8_t portStr[16] = {0};
    sprintf(portStr, "%d", qx_cfg->qx_port);
    int32_t r = uv_getaddrinfo(getLoop(), &resolver, onQxDNSResolve, qx_cfg->qx_service_name, portStr, NULL);
    if (r)
    {
        LOG_ERROR("解析千寻域名失败：%s；重新解析", uv_err_name(r));
        halGpsMainWorkSetTimeout(getQxAddrInfoAndConnect, QX_RE_RESOLVE_TIMEOUT);
    }
}

static void onQxDNSResolve(uv_getaddrinfo_t *handle, int32_t status, struct addrinfo *resp)
{
    if (status < 0)
    {
        LOG_ERROR("解析域名回调失败 %s", uv_err_name(status));
        halGpsMainWorkSetTimeout(getQxAddrInfoAndConnect, QX_RE_RESOLVE_TIMEOUT);
        goto FREE_STH;
    }

    QxConfig *qx_cfg = get_qx_config();

    uv_ip4_name((struct sockaddr_in *)resp->ai_addr, qx_addr, 16);
    LOG_INFO("千寻解析的ip地址：%s，端口：%d", qx_addr, qx_cfg->qx_port);

    halGpsConnect(qx_addr, qx_cfg->qx_port, onQxConnect);

FREE_STH:
    uv_freeaddrinfo(resp);
}

// 千寻服务器重连间隔
#define QX_RECONNECT_TIME_OUT 2 * 1000
static void onQxConnect(uv_connect_t *client, int32_t status)
{
    if (status < 0)
    {
        QxConfig *qx_cfg = get_qx_config();
        LOG_ERROR("千寻服务器 %s:%d 链接失败， 原因：%s", qx_addr, qx_cfg->qx_port, uv_strerror(status));
        halGpsMainWorkSetTimeout(getQxAddrInfoAndConnect, QX_RECONNECT_TIME_OUT);
        return;
    }

    audioQianConnected();

    // 接收事件注册
    uv_read_start(client->handle, alloc_buffer, qx_recv_response);

    qxAuth();
}

static void lostConnectHandle(int32_t status)
{
    if (status)
    {
        const uint8_t *err_str = uv_err_name(status);
        LOG_ERROR("千寻服务器socket写失败或者无回应，重新链接， 原因：%s", err_str);

        if (uv_is_closing((uv_handle_t *)gethalGpsClientConnect()->handle) == 0)
        {
            uv_close((uv_handle_t *)gethalGpsClientConnect()->handle, NULL);
        }
        halGpsMainWorkSetTimeout(getQxAddrInfoAndConnect, QX_RE_RESOLVE_TIMEOUT);

        isQxAuthPass = false;
    }
}

// 服务器写socket回调
static void hal_gps_write_cb(uv_write_t *req, int32_t status)
{
    lostConnectHandle(status);
    free_write_req(req);
}

// 硬件写socket回调
static void hal_gps_hard_ware_write_cb(uv_write_t *req, int32_t status)
{
    free_write_req(req);
}

static void qxAuth()
{
    QxConfig *qx_cfg = get_qx_config();
    uint8_t auth_buff_format[] = "GET /%s HTTP/1.0\r\nUser-Agent: %s\r\nAccept: %s\r\nConnection: %s\r\nAuthorization: Basic %s\r\n\r\n";

    uint8_t *auth_buff = (uint8_t *)malloc(1024);
    memset(auth_buff, 0, 1024);

    uint8_t base64_user_passwd[64 + 64] = {0};
    encode(base64_user_passwd, 64 + 64, qx_cfg->qx_user, qx_cfg->qx_password);
    sprintf(auth_buff, auth_buff_format,
            "RTCM32_GGB",
            "NTRIP QX NTRIPClient/20131124",
            "*/*",
            "close",
            base64_user_passwd);
    // LOG_INFO("auth_buff:\n%s\n", auth_buff);
    int32_t auth_len = strlen(auth_buff);
    // LOG_INFO("auth_len:%d", auth_len);

    write_req_t *wr_rq = (write_req_t *)malloc(sizeof(write_req_t));
    wr_rq->buf = uv_buf_init((uint8_t *)auth_buff, auth_len);

    uv_connect_t *client = gethalGpsClientConnect();
    uv_write((uv_write_t *)wr_rq, client->handle, &wr_rq->buf, 1, hal_gps_write_cb);
}

static void qx_recv_response(uv_stream_t *clientStream, ssize_t nread, const uv_buf_t *buf)
{
    qx_last_recv_time = time(NULL);
    // LOG_INFO("qx_recv_response nread:%d", nread);
    if (nread > 0)
    {
        // LOG_INFO("收到千寻服务器报文的内容：");
        // hzlog_info(userLog, buf->base, nread);
        if (isQxAuthPass == false)
        {

            if (strstr(buf->base, "ICY 200 OK"))
            {
                LOG_INFO("千寻服务器验证成功.");
                isQxAuthPass = true;
                static bool first = true;
                if (first)
                {
                    unsigned int qx_report_interval = get_qx_config()->qx_report_interval * 1000;
                    uv_timer_init(getLoop(), &qx_report_timer);
                    uv_timer_start(&qx_report_timer, reportQxServerTimeOutCb, qx_report_interval, qx_report_interval);
                    first = false;
                }
            }
        }
        else
        {
            // 千寻回复的写回给硬件
            write(qxFd, buf->base, buf->len);
        }
    }
    else if (nread < 0)
    {
        if (nread != UV_EOF)
        {
            LOG_ERROR("读千寻服务器socket错误 %s", uv_err_name(nread));
            // laneClearInterval();
            uv_close((uv_handle_t *)gethalGpsClientConnect()->handle, NULL);
            halGpsMainWorkSetTimeout(getQxAddrInfoAndConnect, QX_RE_RESOLVE_TIMEOUT);
        }
    }

    free(buf->base);
}
// 1节=1海里/小时=1.852公里/小时=0.514444m/s
#define GPS_SPEED_PI (0.514444)

static double calc_gps_speed(struct minmea_float *rmc_speed)
{
    double value = rmc_speed->value;
    double scale = rmc_speed->scale;
    double speed;
    if (scale == 0)
    {
        speed = 0.0;
    }
    else
    {
        speed = (value / scale) * GPS_SPEED_PI;
    }
    return speed;
}

static void notice_speed_event_is_true(uv_timer_t *handle)
{
    if (is_gps_speed_event_true_notice)
    {
        return;
    }
    LOG_INFO("通知图片上传标记为true");
    dispatchSpeedEvent(true);
    is_gps_speed_event_true_notice = true;
    is_gps_speed_event_false_notice = false;
}

static void notice_speed_event_is_false()
{
    if (is_gps_speed_event_false_notice)
    {
        return;
    }
    halGpsSpeedTimerStop();
    LOG_INFO("通知图片上传标记为false");
    dispatchSpeedEvent(false);
    is_gps_speed_event_false_notice = true;
    is_gps_speed_event_true_notice = false;
}

static void stop_pic_upload_event()
{
    if (is_sql_function_init() == false)
    {
        LOG_INFO("sql_function is not init ");
        return;
    }
    notice_speed_event_is_false();
}